//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Fri Nov 26 14:12:01 PST 1999
// Last Modified: Sat Apr 21 10:52:19 PDT 2018 Removed using namespace std;
// Filename:      midifile/include/MidiFile.h
// Website:       http://midifile.sapp.org
// Syntax:        C++11
// vim:           ts=3 noexpandtab
//
// Description:   A class that can read/write Standard MIDI files.
//                MIDI data is stored by track in an array.
//

#ifndef _MIDIFILE_H_INCLUDED
#define _MIDIFILE_H_INCLUDED

#include "MidiEventList.h"

#include <vector>
#include <string>
#include <istream>
#include <fstream>

#define TIME_STATE_DELTA       0
#define TIME_STATE_ABSOLUTE    1

#define TRACK_STATE_SPLIT      0
#define TRACK_STATE_JOINED     1

namespace smf {

    class _TickTime {
    public:
        int tick;
        double seconds;
    };


    class MidiFile {
    public:
        MidiFile(void);

        MidiFile(const std::string &filename);

        MidiFile(std::istream &input);

        MidiFile(const MidiFile &other);

        MidiFile(MidiFile &&other);

        ~MidiFile();

        MidiFile &operator=(const MidiFile &other);

        MidiFile &operator=(MidiFile &&other);

        // reading/writing functions:
        bool read(const std::string &filename);

        bool read(std::istream &instream);

        bool write(const std::string &filename);

        bool write(std::ostream &out);

        bool writeHex(const std::string &filename,
                      int width = 25);

        bool writeHex(std::ostream &out,
                      int width = 25);

        bool writeBinasc(const std::string &filename);

        bool writeBinasc(std::ostream &out);

        bool writeBinascWithComments(const std::string &filename);

        bool writeBinascWithComments(std::ostream &out);

        bool status(void) const;

        // track-related functions:
        const MidiEventList &operator[](int aTrack) const;

        MidiEventList &operator[](int aTrack);

        int getTrackCount(void) const;

        int getNumTracks(void) const;

        int size(void) const;

        void removeEmpties(void);

        // tick-related functions:
        void makeDeltaTicks(void);

        void deltaTicks(void);

        void makeAbsoluteTicks(void);

        void absoluteTicks(void);

        int getTickState(void) const;

        bool isDeltaTicks(void) const;

        bool isAbsoluteTicks(void) const;

        // join/split track functionality:
        void joinTracks(void);

        void splitTracks(void);

        void splitTracksByChannel(void);

        int getTrackState(void) const;

        int hasJoinedTracks(void) const;

        int hasSplitTracks(void) const;

        int getSplitTrack(int track, int index) const;

        int getSplitTrack(int index) const;

        // track sorting funcionality:
        void sortTrack(int track);

        void sortTracks(void);

        void markSequence(void);

        void markSequence(int track, int sequence = 1);

        void clearSequence(void);

        void clearSequence(int track);

        // track manipulation functionality:
        int addTrack(void);

        int addTrack(int count);

        int addTracks(int count);

        void deleteTrack(int aTrack);

        void mergeTracks(int aTrack1, int aTrack2);

        int getTrackCountAsType1(void);

        // ticks-per-quarter related functions:
        void setMillisecondTicks(void);

        int getTicksPerQuarterNote(void) const;

        int getTPQ(void) const;

        void setTicksPerQuarterNote(int ticks);

        void setTPQ(int ticks);

        // physical-time analysis functions:
        void doTimeAnalysis(void);

        double getTimeInSeconds(int aTrack, int anIndex);

        double getTimeInSeconds(int tickvalue);

        double getAbsoluteTickTime(double starttime);

        int getFileDurationInTicks(void);

        double getFileDurationInQuarters(void);

        double getFileDurationInSeconds(void);

        // note-analysis functions:
        int linkNotePairs(void);

        int linkEventPairs(void);

        void clearLinks(void);

        // filename functions:
        void setFilename(const std::string &aname);

        const char *getFilename(void) const;

        // event functionality:
        MidiEvent *addEvent(int aTrack, int aTick,
                            std::vector<uchar> &midiData);

        MidiEvent *addEvent(MidiEvent &mfevent);

        MidiEvent *addEvent(int aTrack, MidiEvent &mfevent);

        MidiEvent &getEvent(int aTrack, int anIndex);

        const MidiEvent &getEvent(int aTrack, int anIndex) const;

        int getEventCount(int aTrack) const;

        int getNumEvents(int aTrack) const;

        void allocateEvents(int track, int aSize);

        void erase(void);

        void clear(void);

        void clear_no_deallocate(void);

        // MIDI message adding convenience functions:
        MidiEvent *addNoteOn(int aTrack, int aTick,
                             int aChannel, int key,
                             int vel);

        MidiEvent *addNoteOff(int aTrack, int aTick,
                              int aChannel, int key,
                              int vel);

        MidiEvent *addNoteOff(int aTrack, int aTick,
                              int aChannel, int key);

        MidiEvent *addController(int aTrack, int aTick,
                                 int aChannel, int num,
                                 int value);

        MidiEvent *addPatchChange(int aTrack, int aTick,
                                  int aChannel, int patchnum);

        MidiEvent *addTimbre(int aTrack, int aTick,
                             int aChannel, int patchnum);

        MidiEvent *addPitchBend(int aTrack, int aTick,
                                int aChannel, double amount);

        // Controller message adding convenience functions:
        MidiEvent *addSustain(int aTrack, int aTick,
                              int aChannel, int value);

        MidiEvent *addSustainPedal(int aTrack, int aTick,
                                   int aChannel, int value);

        MidiEvent *addSustainOn(int aTrack, int aTick,
                                int aChannel);

        MidiEvent *addSustainPedalOn(int aTrack, int aTick,
                                     int aChannel);

        MidiEvent *addSustainOff(int aTrack, int aTick,
                                 int aChannel);

        MidiEvent *addSustainPedalOff(int aTrack, int aTick,
                                      int aChannel);

        // Meta-event adding convenience functions:
        MidiEvent *addMetaEvent(int aTrack, int aTick,
                                int aType,
                                std::vector<uchar> &metaData);

        MidiEvent *addMetaEvent(int aTrack, int aTick,
                                int aType,
                                const std::string &metaData);

        MidiEvent *addText(int aTrack, int aTick,
                           const std::string &text);

        MidiEvent *addCopyright(int aTrack, int aTick,
                                const std::string &text);

        MidiEvent *addTrackName(int aTrack, int aTick,
                                const std::string &name);

        MidiEvent *addInstrumentName(int aTrack, int aTick,
                                     const std::string &name);

        MidiEvent *addLyric(int aTrack, int aTick,
                            const std::string &text);

        MidiEvent *addMarker(int aTrack, int aTick,
                             const std::string &text);

        MidiEvent *addCue(int aTrack, int aTick,
                          const std::string &text);

        MidiEvent *addTempo(int aTrack, int aTick,
                            double aTempo);

        MidiEvent *addTimeSignature(int aTrack, int aTick,
                                    int top, int bottom,
                                    int clocksPerClick = 24,
                                    int num32dsPerQuarter = 8);

        MidiEvent *addCompoundTimeSignature(int aTrack, int aTick,
                                            int top, int bottom,
                                            int clocksPerClick = 36,
                                            int num32dsPerQuarter = 8);

        uchar readByte(std::istream &input);

        // static functions:
        static ushort readLittleEndian2Bytes(std::istream &input);

        static ulong readLittleEndian4Bytes(std::istream &input);

        static std::ostream &writeLittleEndianUShort(std::ostream &out,
                                                     ushort value);

        static std::ostream &writeBigEndianUShort(std::ostream &out,
                                                  ushort value);

        static std::ostream &writeLittleEndianShort(std::ostream &out,
                                                    short value);

        static std::ostream &writeBigEndianShort(std::ostream &out,
                                                 short value);

        static std::ostream &writeLittleEndianULong(std::ostream &out,
                                                    ulong value);

        static std::ostream &writeBigEndianULong(std::ostream &out,
                                                 ulong value);

        static std::ostream &writeLittleEndianLong(std::ostream &out,
                                                   long value);

        static std::ostream &writeBigEndianLong(std::ostream &out,
                                                long value);

        static std::ostream &writeLittleEndianFloat(std::ostream &out,
                                                    float value);

        static std::ostream &writeBigEndianFloat(std::ostream &out,
                                                 float value);

        static std::ostream &writeLittleEndianDouble(std::ostream &out,
                                                     double value);

        static std::ostream &writeBigEndianDouble(std::ostream &out,
                                                  double value);

    protected:
        // m_events == Lists of MidiEvents for each MIDI file track.
        std::vector<MidiEventList *> m_events;

        // m_ticksPerQuarterNote == A value for the MIDI file header
        // which represents the number of ticks in a quarter note
        // that are used as units for the delta times for MIDI events
        // in MIDI file track data.
        int m_ticksPerQuarterNote = 120;

        // m_trackCount == the number of tracks in the file.
        int m_trackCount = 1;

        // m_theTrackState == state variable for whether the tracks
        // are joined or split.
        int m_theTrackState = TRACK_STATE_SPLIT;

        // m_theTimeState == state variable for whether the MidiEvent::tick
        // variable contain absolute ticks since the start of the file's
        // time, or delta ticks since the last MIDI event in the track.
        int m_theTimeState = TIME_STATE_ABSOLUTE;

        // m_readFileName == the filename of the last file read into
        // the object.
        std::string m_readFileName;

        // m_timemapvalid ==
        bool m_timemapvalid = false;

        // m_timemap ==
        std::vector<_TickTime> m_timemap;

        // m_rwstatus == True if last read was successful, false if a problem.
        bool m_rwstatus = true;

        // m_linkedEventQ == True if link analysis has been done.
        bool m_linkedEventsQ = false;

    private:
        int extractMidiData(std::istream &inputfile,
                            std::vector<uchar> &array,
                            uchar &runningCommand);

        ulong readVLValue(std::istream &inputfile);

        ulong unpackVLV(uchar a = 0, uchar b = 0,
                        uchar c = 0, uchar d = 0,
                        uchar e = 0);

        void writeVLValue(long aValue,
                          std::vector<uchar> &data);

        int makeVLV(uchar *buffer, int number);

        static int ticksearch(const void *A, const void *B);

        static int secondsearch(const void *A, const void *B);

        void buildTimeMap(void);

        double linearTickInterpolationAtSecond(double seconds);

        double linearSecondInterpolationAtTick(int ticktime);
    };

} // end of namespace smf

std::ostream &operator<<(std::ostream &out, smf::MidiFile &aMidiFile);

#endif /* _MIDIFILE_H_INCLUDED */



